// -*- C++ -*-
//===--------------------------- complex ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCUDACXX_COMPLEX
#define _LIBCUDACXX_COMPLEX

/*
    complex synopsis

namespace std
{

template<class T>
class complex
{
public:
    typedef T value_type;

    complex(const T& re = T(), const T& im = T()); // constexpr in C++14
    complex(const complex&);  // constexpr in C++14
    template<class X> complex(const complex<X>&);  // constexpr in C++14

    T real() const; // constexpr in C++14
    T imag() const; // constexpr in C++14

    void real(T); // constexpr in C++20
    void imag(T); // constexpr in C++20

    complex<T>& operator= (const T&); // constexpr in C++20
    complex<T>& operator+=(const T&); // constexpr in C++20
    complex<T>& operator-=(const T&); // constexpr in C++20
    complex<T>& operator*=(const T&); // constexpr in C++20
    complex<T>& operator/=(const T&); // constexpr in C++20

    complex& operator=(const complex&); // constexpr in C++20
    template<class X> complex<T>& operator= (const complex<X>&); // constexpr in C++20
    template<class X> complex<T>& operator+=(const complex<X>&); // constexpr in C++20
    template<class X> complex<T>& operator-=(const complex<X>&); // constexpr in C++20
    template<class X> complex<T>& operator*=(const complex<X>&); // constexpr in C++20
    template<class X> complex<T>& operator/=(const complex<X>&); // constexpr in C++20
};

template<>
class complex<float>
{
public:
    typedef float value_type;

    constexpr complex(float re = 0.0f, float im = 0.0f);
    explicit constexpr complex(const complex<double>&);
    explicit constexpr complex(const complex<long double>&);

    constexpr float real() const;
    void real(float); // constexpr in C++20
    constexpr float imag() const;
    void imag(float); // constexpr in C++20

    complex<float>& operator= (float); // constexpr in C++20
    complex<float>& operator+=(float); // constexpr in C++20
    complex<float>& operator-=(float); // constexpr in C++20
    complex<float>& operator*=(float); // constexpr in C++20
    complex<float>& operator/=(float); // constexpr in C++20

    complex<float>& operator=(const complex<float>&); // constexpr in C++20
    template<class X> complex<float>& operator= (const complex<X>&); // constexpr in C++20
    template<class X> complex<float>& operator+=(const complex<X>&); // constexpr in C++20
    template<class X> complex<float>& operator-=(const complex<X>&); // constexpr in C++20
    template<class X> complex<float>& operator*=(const complex<X>&); // constexpr in C++20
    template<class X> complex<float>& operator/=(const complex<X>&); // constexpr in C++20
};

template<>
class complex<double>
{
public:
    typedef double value_type;

    constexpr complex(double re = 0.0, double im = 0.0);
    constexpr complex(const complex<float>&);
    explicit constexpr complex(const complex<long double>&);

    constexpr double real() const;
    void real(double); // constexpr in C++20
    constexpr double imag() const;
    void imag(double); // constexpr in C++20

    complex<double>& operator= (double); // constexpr in C++20
    complex<double>& operator+=(double); // constexpr in C++20
    complex<double>& operator-=(double); // constexpr in C++20
    complex<double>& operator*=(double); // constexpr in C++20
    complex<double>& operator/=(double); // constexpr in C++20
    complex<double>& operator=(const complex<double>&); // constexpr in C++20

    template<class X> complex<double>& operator= (const complex<X>&); // constexpr in C++20
    template<class X> complex<double>& operator+=(const complex<X>&); // constexpr in C++20
    template<class X> complex<double>& operator-=(const complex<X>&); // constexpr in C++20
    template<class X> complex<double>& operator*=(const complex<X>&); // constexpr in C++20
    template<class X> complex<double>& operator/=(const complex<X>&); // constexpr in C++20
};

template<>
class complex<long double>
{
public:
    typedef long double value_type;

    constexpr complex(long double re = 0.0L, long double im = 0.0L);
    constexpr complex(const complex<float>&);
    constexpr complex(const complex<double>&);

    constexpr long double real() const;
    void real(long double); // constexpr in C++20
    constexpr long double imag() const;
    void imag(long double); // constexpr in C++20

    complex<long double>& operator=(const complex<long double>&); // constexpr in C++20
    complex<long double>& operator= (long double); // constexpr in C++20
    complex<long double>& operator+=(long double); // constexpr in C++20
    complex<long double>& operator-=(long double); // constexpr in C++20
    complex<long double>& operator*=(long double); // constexpr in C++20
    complex<long double>& operator/=(long double); // constexpr in C++20

    template<class X> complex<long double>& operator= (const complex<X>&); // constexpr in C++20
    template<class X> complex<long double>& operator+=(const complex<X>&); // constexpr in C++20
    template<class X> complex<long double>& operator-=(const complex<X>&); // constexpr in C++20
    template<class X> complex<long double>& operator*=(const complex<X>&); // constexpr in C++20
    template<class X> complex<long double>& operator/=(const complex<X>&); // constexpr in C++20
};

// 26.3.6 operators:
template<class T> complex<T> operator+(const complex<T>&, const complex<T>&); // constexpr in C++20
template<class T> complex<T> operator+(const complex<T>&, const T&);          // constexpr in C++20
template<class T> complex<T> operator+(const T&, const complex<T>&);          // constexpr in C++20
template<class T> complex<T> operator-(const complex<T>&, const complex<T>&); // constexpr in C++20
template<class T> complex<T> operator-(const complex<T>&, const T&);          // constexpr in C++20
template<class T> complex<T> operator-(const T&, const complex<T>&);          // constexpr in C++20
template<class T> complex<T> operator*(const complex<T>&, const complex<T>&); // constexpr in C++20
template<class T> complex<T> operator*(const complex<T>&, const T&);          // constexpr in C++20
template<class T> complex<T> operator*(const T&, const complex<T>&);          // constexpr in C++20
template<class T> complex<T> operator/(const complex<T>&, const complex<T>&); // constexpr in C++20
template<class T> complex<T> operator/(const complex<T>&, const T&);          // constexpr in C++20
template<class T> complex<T> operator/(const T&, const complex<T>&);          // constexpr in C++20
template<class T> complex<T> operator+(const complex<T>&);                    // constexpr in C++20
template<class T> complex<T> operator-(const complex<T>&);                    // constexpr in C++20
template<class T> bool operator==(const complex<T>&, const complex<T>&); // constexpr in C++14
template<class T> bool operator==(const complex<T>&, const T&); // constexpr in C++14
template<class T> bool operator==(const T&, const complex<T>&); // constexpr in C++14
template<class T> bool operator!=(const complex<T>&, const complex<T>&); // constexpr in C++14
template<class T> bool operator!=(const complex<T>&, const T&); // constexpr in C++14
template<class T> bool operator!=(const T&, const complex<T>&); // constexpr in C++14

template<class T, class charT, class traits>
  basic_istream<charT, traits>&
  operator>>(basic_istream<charT, traits>&, complex<T>&);
template<class T, class charT, class traits>
  basic_ostream<charT, traits>&
  operator<<(basic_ostream<charT, traits>&, const complex<T>&);

// 26.3.7 values:

template<class T>              T real(const complex<T>&); // constexpr in C++14
                     long double real(long double);       // constexpr in C++14
                          double real(double);            // constexpr in C++14
template<Integral T>      double real(T);                 // constexpr in C++14
                          float  real(float);             // constexpr in C++14

template<class T>              T imag(const complex<T>&); // constexpr in C++14
                     long double imag(long double);       // constexpr in C++14
                          double imag(double);            // constexpr in C++14
template<Integral T>      double imag(T);                 // constexpr in C++14
                          float  imag(float);             // constexpr in C++14

template<class T> T abs(const complex<T>&);

template<class T>              T arg(const complex<T>&);
                     long double arg(long double);
                          double arg(double);
template<Integral T>      double arg(T);
                          float  arg(float);

template<class T>              T norm(const complex<T>&); // constexpr in C++20
                     long double norm(long double);       // constexpr in C++20
                          double norm(double);            // constexpr in C++20
template<Integral T>      double norm(T);                 // constexpr in C++20
                          float  norm(float);             // constexpr in C++20

template<class T>      complex<T>           conj(const complex<T>&); // constexpr in C++20
                       complex<long double> conj(long double);       // constexpr in C++20
                       complex<double>      conj(double);            // constexpr in C++20
template<Integral T>   complex<double>      conj(T);                 // constexpr in C++20
                       complex<float>       conj(float);             // constexpr in C++20

template<class T>    complex<T>           proj(const complex<T>&);
                     complex<long double> proj(long double);
                     complex<double>      proj(double);
template<Integral T> complex<double>      proj(T);
                     complex<float>       proj(float);

template<class T> complex<T> polar(const T&, const T& = T());

// 26.3.8 transcendentals:
template<class T> complex<T> acos(const complex<T>&);
template<class T> complex<T> asin(const complex<T>&);
template<class T> complex<T> atan(const complex<T>&);
template<class T> complex<T> acosh(const complex<T>&);
template<class T> complex<T> asinh(const complex<T>&);
template<class T> complex<T> atanh(const complex<T>&);
template<class T> complex<T> cos (const complex<T>&);
template<class T> complex<T> cosh (const complex<T>&);
template<class T> complex<T> exp (const complex<T>&);
template<class T> complex<T> log (const complex<T>&);
template<class T> complex<T> log10(const complex<T>&);

template<class T> complex<T> pow(const complex<T>&, const T&);
template<class T> complex<T> pow(const complex<T>&, const complex<T>&);
template<class T> complex<T> pow(const T&, const complex<T>&);

template<class T> complex<T> sin (const complex<T>&);
template<class T> complex<T> sinh (const complex<T>&);
template<class T> complex<T> sqrt (const complex<T>&);
template<class T> complex<T> tan (const complex<T>&);
template<class T> complex<T> tanh (const complex<T>&);

}  // std

*/

#include <cuda/std/detail/__config>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
#  pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
#  pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
#  pragma system_header
#endif // no system header

#include <cuda/std/__complex/vector_support.h>
#include <cuda/std/__concepts/concept_macros.h>
#include <cuda/std/__fwd/get.h>
#include <cuda/std/__tuple_dir/tuple_element.h>
#include <cuda/std/__tuple_dir/tuple_size.h>
#include <cuda/std/__type_traits/enable_if.h>
#include <cuda/std/__type_traits/is_constructible.h>
#include <cuda/std/__type_traits/is_floating_point.h>
#include <cuda/std/__type_traits/is_integral.h>
#include <cuda/std/__type_traits/is_same.h>
#include <cuda/std/cmath>
#include <cuda/std/cstdint>
#include <cuda/std/limits>
#include <cuda/std/type_traits>
#include <cuda/std/version>

// Compatibility helpers for thrust to convert between `std::complex` and `cuda::std::complex`
#if !_CCCL_COMPILER(NVRTC)
#  include <complex>
#  include <sstream> // for std::basic_ostringstream

#  define _LIBCUDACXX_ACCESS_STD_COMPLEX_REAL(__c) reinterpret_cast<const _Up(&)[2]>(__c)[0]
#  define _LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG(__c) reinterpret_cast<const _Up(&)[2]>(__c)[1]
#endif // !_CCCL_COMPILER(NVRTC)

#include <cuda/std/__cccl/prologue.h>

#ifdef LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_OPERATIONS
#  ifndef LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_MULTIPLICATION
#    define LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_MULTIPLICATION
#  endif // LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_MULTIPLICATION
#  ifndef LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_DIVISION
#    define LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_DIVISION
#  endif // LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_DIVISION
#endif // LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_OPERATIONS

_LIBCUDACXX_BEGIN_NAMESPACE_STD

template <class _Tp>
class _CCCL_TYPE_VISIBILITY_DEFAULT _LIBCUDACXX_COMPLEX_ALIGNAS complex
{
  _Tp __re_;
  _Tp __im_;

  template <class _Up>
  friend class complex;

  template <class _Up>
  friend struct __get_complex_impl;

public:
  using value_type = _Tp;

  _CCCL_API constexpr complex(const value_type& __re = value_type(), const value_type& __im = value_type())
      : __re_(__re)
      , __im_(__im)
  {}

  template <class _Up, enable_if_t<__cccl_internal::__is_non_narrowing_convertible<_Tp, _Up>::value, int> = 0>
  _CCCL_API constexpr complex(const complex<_Up>& __c)
      : __re_(static_cast<_Tp>(__c.real()))
      , __im_(static_cast<_Tp>(__c.imag()))
  {}

  template <class _Up,
            enable_if_t<!__cccl_internal::__is_non_narrowing_convertible<_Tp, _Up>::value, int> = 0,
            enable_if_t<_CCCL_TRAIT(is_constructible, _Tp, _Up), int>                           = 0>
  _CCCL_API explicit constexpr complex(const complex<_Up>& __c)
      : __re_(static_cast<_Tp>(__c.real()))
      , __im_(static_cast<_Tp>(__c.imag()))
  {}

  _CCCL_API constexpr complex& operator=(const value_type& __re)
  {
    __re_ = __re;
    __im_ = value_type();
    return *this;
  }

  template <class _Up>
  _CCCL_API constexpr complex& operator=(const complex<_Up>& __c)
  {
    __re_ = __c.real();
    __im_ = __c.imag();
    return *this;
  }

#if !_CCCL_COMPILER(NVRTC)
  template <class _Up>
  _CCCL_API inline complex(const ::std::complex<_Up>& __other)
      : __re_(_LIBCUDACXX_ACCESS_STD_COMPLEX_REAL(__other))
      , __im_(_LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG(__other))
  {}

  template <class _Up>
  _CCCL_API inline complex& operator=(const ::std::complex<_Up>& __other)
  {
    __re_ = _LIBCUDACXX_ACCESS_STD_COMPLEX_REAL(__other);
    __im_ = _LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG(__other);
    return *this;
  }

  _CCCL_HOST constexpr operator ::std::complex<_Tp>() const
  {
    return {__re_, __im_};
  }
#endif // !_CCCL_COMPILER(NVRTC)

  [[nodiscard]] _CCCL_API constexpr value_type real() const
  {
    return __re_;
  }
  [[nodiscard]] _CCCL_API constexpr value_type imag() const
  {
    return __im_;
  }

  _CCCL_API constexpr void real(value_type __re)
  {
    __re_ = __re;
  }
  _CCCL_API constexpr void imag(value_type __im)
  {
    __im_ = __im;
  }

  // Those additional volatile overloads are meant to help with reductions in thrust
  [[nodiscard]] _CCCL_API inline value_type real() const volatile
  {
    return __re_;
  }
  [[nodiscard]] _CCCL_API inline value_type imag() const volatile
  {
    return __im_;
  }

  _CCCL_API inline void real(value_type __re) volatile
  {
    __re_ = __re;
  }
  _CCCL_API inline void imag(value_type __im) volatile
  {
    __im_ = __im;
  }

  _CCCL_API constexpr complex& operator+=(const value_type& __re)
  {
    __re_ += __re;
    return *this;
  }
  _CCCL_API constexpr complex& operator-=(const value_type& __re)
  {
    __re_ -= __re;
    return *this;
  }
  _CCCL_API constexpr complex& operator*=(const value_type& __re)
  {
    __re_ *= __re;
    __im_ *= __re;
    return *this;
  }
  _CCCL_API constexpr complex& operator/=(const value_type& __re)
  {
    __re_ /= __re;
    __im_ /= __re;
    return *this;
  }

  template <class _Up>
  _CCCL_API constexpr complex& operator+=(const complex<_Up>& __c)
  {
    __re_ += __c.real();
    __im_ += __c.imag();
    return *this;
  }
  template <class _Up>
  _CCCL_API constexpr complex& operator-=(const complex<_Up>& __c)
  {
    __re_ -= __c.real();
    __im_ -= __c.imag();
    return *this;
  }
};

template <class _Tp>
inline constexpr bool __is_complex_v = false;

template <class _Tp>
inline constexpr bool __is_complex_v<complex<_Tp>> = true;

template <class _Tp, class _Up>
_CCCL_API inline _CCCL_CONSTEXPR_CXX14_COMPLEX complex<_Tp>& operator*=(complex<_Tp>& __lhs, const complex<_Up>& __rhs)
{
  __lhs = __lhs * complex<_Tp>(__rhs.real(), __rhs.imag());
  return __lhs;
}
template <class _Tp, class _Up>
_CCCL_API inline _CCCL_CONSTEXPR_CXX14_COMPLEX complex<_Tp>& operator/=(complex<_Tp>& __lhs, const complex<_Up>& __rhs)
{
  __lhs = __lhs / complex<_Tp>(__rhs.real(), __rhs.imag());
  return __lhs;
}

// 26.3.6 operators:
template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator+(const complex<_Tp>& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(__x);
  __t += __y;
  return __t;
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator+(const complex<_Tp>& __x, const _Tp& __y)
{
  complex<_Tp> __t(__x);
  __t += __y;
  return __t;
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator+(const _Tp& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(__y);
  __t += __x;
  return __t;
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator-(const complex<_Tp>& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(__x);
  __t -= __y;
  return __t;
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator-(const complex<_Tp>& __x, const _Tp& __y)
{
  complex<_Tp> __t(__x);
  __t -= __y;
  return __t;
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator-(const _Tp& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(-__y);
  __t += __x;
  return __t;
}
template <class _Tp>
[[nodiscard]] _CCCL_API inline _CCCL_CONSTEXPR_CXX14_COMPLEX complex<_Tp>
operator*(const complex<_Tp>& __z, const complex<_Tp>& __w)
{
  _Tp __a = __z.real();
  _Tp __b = __z.imag();
  _Tp __c = __w.real();
  _Tp __d = __w.imag();

#if defined(_CCCL_BUILTIN_IS_CONSTANT_EVALUATED)
  // Avoid floating point operations that are invalid during constant evaluation
  if (_CUDA_VSTD::is_constant_evaluated())
  {
    bool __z_zero = __a == _Tp(0) && __b == _Tp(0);
    bool __w_zero = __c == _Tp(0) && __d == _Tp(0);
    bool __z_inf  = _CUDA_VSTD::isinf(__a) || _CUDA_VSTD::isinf(__b);
    bool __w_inf  = _CUDA_VSTD::isinf(__c) || _CUDA_VSTD::isinf(__d);
    bool __z_nan  = !__z_inf
                && ((_CUDA_VSTD::isnan(__a) && _CUDA_VSTD::isnan(__b)) || (_CUDA_VSTD::isnan(__a) && __b == _Tp(0))
                    || (__a == _Tp(0) && _CUDA_VSTD::isnan(__b)));
    bool __w_nan = !__w_inf
                && ((_CUDA_VSTD::isnan(__c) && _CUDA_VSTD::isnan(__d)) || (_CUDA_VSTD::isnan(__c) && __d == _Tp(0))
                    || (__c == _Tp(0) && _CUDA_VSTD::isnan(__d)));
    if (__z_nan || __w_nan)
    {
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
    }
    if (__z_inf || __w_inf)
    {
      if (__z_zero || __w_zero)
      {
        return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
      }
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
    }
    bool __z_nonzero_nan = !__z_inf && !__z_nan && (_CUDA_VSTD::isnan(__a) || _CUDA_VSTD::isnan(__b));
    bool __w_nonzero_nan = !__w_inf && !__w_nan && (_CUDA_VSTD::isnan(__c) || _CUDA_VSTD::isnan(__d));
    if (__z_nonzero_nan || __w_nonzero_nan)
    {
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
    }
  }
#endif // _CCCL_BUILTIN_IS_CONSTANT_EVALUATED

  __abcd_results<_Tp> __partials = __complex_calculate_partials(__a, __b, __c, __d);

  _Tp __x = __partials.__ac - __partials.__bd;
  _Tp __y = __partials.__ad + __partials.__bc;
#ifndef LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_MULTIPLICATION
  if (_CUDA_VSTD::isnan(__x) && _CUDA_VSTD::isnan(__y))
  {
    bool __recalc = false;
    if (_CUDA_VSTD::isinf(__a) || _CUDA_VSTD::isinf(__b))
    {
      __a = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::isinf(__a) ? _Tp(1) : _Tp(0), __a);
      __b = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::isinf(__b) ? _Tp(1) : _Tp(0), __b);
      if (_CUDA_VSTD::isnan(__c))
      {
        __c = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __c);
      }
      if (_CUDA_VSTD::isnan(__d))
      {
        __d = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __d);
      }
      __recalc = true;
    }
    if (_CUDA_VSTD::isinf(__c) || _CUDA_VSTD::isinf(__d))
    {
      __c = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::isinf(__c) ? _Tp(1) : _Tp(0), __c);
      __d = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::isinf(__d) ? _Tp(1) : _Tp(0), __d);
      if (_CUDA_VSTD::isnan(__a))
      {
        __a = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __a);
      }
      if (_CUDA_VSTD::isnan(__b))
      {
        __b = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __b);
      }
      __recalc = true;
    }
    if (!__recalc
        && (_CUDA_VSTD::isinf(__partials.__ac) || _CUDA_VSTD::isinf(__partials.__bd)
            || _CUDA_VSTD::isinf(__partials.__ad) || _CUDA_VSTD::isinf(__partials.__bc)))
    {
      if (_CUDA_VSTD::isnan(__a))
      {
        __a = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __a);
      }
      if (_CUDA_VSTD::isnan(__b))
      {
        __b = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __b);
      }
      if (_CUDA_VSTD::isnan(__c))
      {
        __c = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __c);
      }
      if (_CUDA_VSTD::isnan(__d))
      {
        __d = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __d);
      }
      __recalc = true;
    }
    if (__recalc)
    {
      __partials = __complex_calculate_partials(__a, __b, __c, __d);

      __x = numeric_limits<_Tp>::infinity() * (__partials.__ac - __partials.__bd);
      __y = numeric_limits<_Tp>::infinity() * (__partials.__ad + __partials.__bc);
    }
  }
#endif // LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_MULTIPLICATION
  return complex<_Tp>(__x, __y);
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator*(const complex<_Tp>& __x, const _Tp& __y)
{
  complex<_Tp> __t(__x);
  __t *= __y;
  return __t;
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator*(const _Tp& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(__y);
  __t *= __x;
  return __t;
}

template <class _Tp>
[[nodiscard]] _CCCL_API inline _CCCL_CONSTEXPR_CXX14_COMPLEX complex<_Tp>
operator/(const complex<_Tp>& __z, const complex<_Tp>& __w)
{
  int __ilogbw = 0;
  _Tp __a      = __z.real();
  _Tp __b      = __z.imag();
  _Tp __c      = __w.real();
  _Tp __d      = __w.imag();
  _Tp __logbw  = _CUDA_VSTD::__constexpr_logb(
    _CUDA_VSTD::__constexpr_fmax(_CUDA_VSTD::__constexpr_fabs(__c), _CUDA_VSTD::__constexpr_fabs(__d)));
  if (_CUDA_VSTD::isfinite(__logbw))
  {
    __ilogbw = static_cast<int>(__logbw);
    __c      = _CUDA_VSTD::__constexpr_scalbn(__c, -__ilogbw);
    __d      = _CUDA_VSTD::__constexpr_scalbn(__d, -__ilogbw);
  }

#if defined(_CCCL_BUILTIN_IS_CONSTANT_EVALUATED)
  // Avoid floating point operations that are invalid during constant evaluation
  if (_CUDA_VSTD::is_constant_evaluated())
  {
    bool __z_zero = __a == _Tp(0) && __b == _Tp(0);
    bool __w_zero = __c == _Tp(0) && __d == _Tp(0);
    bool __z_inf  = _CUDA_VSTD::isinf(__a) || _CUDA_VSTD::isinf(__b);
    bool __w_inf  = _CUDA_VSTD::isinf(__c) || _CUDA_VSTD::isinf(__d);
    bool __z_nan  = !__z_inf
                && ((_CUDA_VSTD::isnan(__a) && _CUDA_VSTD::isnan(__b)) || (_CUDA_VSTD::isnan(__a) && __b == _Tp(0))
                    || (__a == _Tp(0) && _CUDA_VSTD::isnan(__b)));
    bool __w_nan = !__w_inf
                && ((_CUDA_VSTD::isnan(__c) && _CUDA_VSTD::isnan(__d)) || (_CUDA_VSTD::isnan(__c) && __d == _Tp(0))
                    || (__c == _Tp(0) && _CUDA_VSTD::isnan(__d)));
    if ((__z_nan || __w_nan) || (__z_inf && __w_inf))
    {
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
    }
    bool __z_nonzero_nan = !__z_inf && !__z_nan && (_CUDA_VSTD::isnan(__a) || _CUDA_VSTD::isnan(__b));
    bool __w_nonzero_nan = !__w_inf && !__w_nan && (_CUDA_VSTD::isnan(__c) || _CUDA_VSTD::isnan(__d));
    if (__z_nonzero_nan || __w_nonzero_nan)
    {
      if (__w_zero)
      {
        return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
      }
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
    }
    if (__w_inf)
    {
      return complex<_Tp>(_Tp(0), _Tp(0));
    }
    if (__z_inf)
    {
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
    }
    if (__w_zero)
    {
      if (__z_zero)
      {
        return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
      }
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
    }
  }
#endif // _CCCL_BUILTIN_IS_CONSTANT_EVALUATED

  __abcd_results<_Tp> __partials = __complex_calculate_partials(__a, __b, __c, __d);
  __ab_results<_Tp> __denom_vec  = __complex_piecewise_mul(__c, __d, __c, __d);

  _Tp __denom = __denom_vec.__a + __denom_vec.__b;
  _Tp __x     = _CUDA_VSTD::__constexpr_scalbn((__partials.__ac + __partials.__bd) / __denom, -__ilogbw);
  _Tp __y     = _CUDA_VSTD::__constexpr_scalbn((__partials.__bc - __partials.__ad) / __denom, -__ilogbw);
#ifndef LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_DIVISION
  if (_CUDA_VSTD::isnan(__x) && _CUDA_VSTD::isnan(__y))
  {
    if ((__denom == _Tp(0)) && (!_CUDA_VSTD::isnan(__a) || !_CUDA_VSTD::isnan(__b)))
    {
      __x = _CUDA_VSTD::__constexpr_copysign(numeric_limits<_Tp>::infinity(), __c) * __a;
      __y = _CUDA_VSTD::__constexpr_copysign(numeric_limits<_Tp>::infinity(), __c) * __b;
    }
    else if ((_CUDA_VSTD::isinf(__a) || _CUDA_VSTD::isinf(__b)) && _CUDA_VSTD::isfinite(__c)
             && _CUDA_VSTD::isfinite(__d))
    {
      __a = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::isinf(__a) ? _Tp(1) : _Tp(0), __a);
      __b = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::isinf(__b) ? _Tp(1) : _Tp(0), __b);
      __x = numeric_limits<_Tp>::infinity() * (__a * __c + __b * __d);
      __y = numeric_limits<_Tp>::infinity() * (__b * __c - __a * __d);
    }
    else if (_CUDA_VSTD::isinf(__logbw) && __logbw > _Tp(0) && _CUDA_VSTD::isfinite(__a) && _CUDA_VSTD::isfinite(__b))
    {
      __c = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::isinf(__c) ? _Tp(1) : _Tp(0), __c);
      __d = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::isinf(__d) ? _Tp(1) : _Tp(0), __d);
      __x = _Tp(0) * (__a * __c + __b * __d);
      __y = _Tp(0) * (__b * __c - __a * __d);
    }
  }
#endif // LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_DIVISION
  return complex<_Tp>(__x, __y);
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator/(const complex<_Tp>& __x, const _Tp& __y)
{
  return complex<_Tp>(__x.real() / __y, __x.imag() / __y);
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator/(const _Tp& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(__x);
  __t /= __y;
  return __t;
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator+(const complex<_Tp>& __x)
{
  return __x;
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> operator-(const complex<_Tp>& __x)
{
  return complex<_Tp>(-__x.real(), -__x.imag());
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr bool operator==(const complex<_Tp>& __x, const complex<_Tp>& __y)
{
  return __x.real() == __y.real() && __x.imag() == __y.imag();
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr bool operator==(const complex<_Tp>& __x, const _Tp& __y)
{
  return __x.real() == __y && __x.imag() == _Tp(0);
}

#if _CCCL_STD_VER <= 2017
template <class _Tp>
[[nodiscard]] _CCCL_API constexpr bool operator==(const _Tp& __x, const complex<_Tp>& __y)
{
  return __x == __y.real() && _Tp(0) == __y.imag();
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr bool operator!=(const complex<_Tp>& __x, const complex<_Tp>& __y)
{
  return !(__x == __y);
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr bool operator!=(const complex<_Tp>& __x, const _Tp& __y)
{
  return !(__x == __y);
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr bool operator!=(const _Tp& __x, const complex<_Tp>& __y)
{
  return !(__x == __y);
}
#endif // _CCCL_STD_VER <= 2017

#if !_CCCL_COMPILER(NVRTC)
template <class _Tp, class _Up>
[[nodiscard]] _CCCL_API constexpr bool operator==(const complex<_Tp>& __x, const ::std::complex<_Up>& __y)
{
  return __x.real() == _LIBCUDACXX_ACCESS_STD_COMPLEX_REAL(__y)
      && __x.imag() == _LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG(__y);
}

#  if _CCCL_STD_VER <= 2017
template <class _Tp, class _Up>
[[nodiscard]] _CCCL_API constexpr bool operator==(const ::std::complex<_Up>& __x, const complex<_Tp>& __y)
{
  return __y.real() == _LIBCUDACXX_ACCESS_STD_COMPLEX_REAL(__x)
      && __y.imag() == _LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG(__x);
}

template <class _Tp, class _Up>
[[nodiscard]] _CCCL_API constexpr bool operator!=(const complex<_Tp>& __x, const ::std::complex<_Up>& __y)
{
  return !(__x == __y);
}

template <class _Tp, class _Up>
[[nodiscard]] _CCCL_API constexpr bool operator!=(const ::std::complex<_Up>& __x, const complex<_Tp>& __y)
{
  return !(__x == __y);
}
#  endif // _CCCL_STD_VER <= 2017
#endif // !_CCCL_COMPILER(NVRTC)

// real

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr _Tp real(const complex<_Tp>& __c)
{
  return __c.real();
}

// 26.3.7 values:

template <class _Tp, bool = _CCCL_TRAIT(is_integral, _Tp), bool = _CCCL_TRAIT(is_floating_point, _Tp)>
struct __cccl_complex_overload_traits
{};

// Integral Types
template <class _Tp>
struct __cccl_complex_overload_traits<_Tp, true, false>
{
  using _ValueType   = double;
  using _ComplexType = complex<double>;
};

// Floating point types
template <class _Tp>
struct __cccl_complex_overload_traits<_Tp, false, true>
{
  using _ValueType   = _Tp;
  using _ComplexType = complex<_Tp>;
};

template <class _Tp>
using __cccl_complex_value_type = typename __cccl_complex_overload_traits<_Tp>::_ValueType;

template <class _Tp>
using __cccl_complex_complex_type = typename __cccl_complex_overload_traits<_Tp>::_ComplexType;

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr __cccl_complex_value_type<_Tp> real(_Tp __re)
{
  return __re;
}

// imag

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr _Tp imag(const complex<_Tp>& __c)
{
  return __c.imag();
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr __cccl_complex_value_type<_Tp> imag(_Tp)
{
  return 0;
}

// abs

template <class _Tp>
[[nodiscard]] _CCCL_API inline _Tp abs(const complex<_Tp>& __c)
{
  return _CUDA_VSTD::hypot(__c.real(), __c.imag());
}

// arg

template <class _Tp>
[[nodiscard]] _CCCL_API inline _Tp arg(const complex<_Tp>& __c)
{
  return _CUDA_VSTD::atan2(__c.imag(), __c.real());
}

[[nodiscard]] _CCCL_API inline float arg(float __re)
{
  return _CUDA_VSTD::atan2f(0.F, __re);
}

[[nodiscard]] _CCCL_API inline double arg(double __re)
{
  return _CUDA_VSTD::atan2(0.0, __re);
}

#if _CCCL_HAS_LONG_DOUBLE()
[[nodiscard]] _CCCL_API inline long double arg(long double __re)
{
  return _CUDA_VSTD::atan2l(0.L, __re);
}
#endif // _CCCL_HAS_LONG_DOUBLE()

_CCCL_TEMPLATE(class _Tp)
_CCCL_REQUIRES(_CCCL_TRAIT(is_integral, _Tp))
[[nodiscard]] _CCCL_API inline double arg(_Tp __re)
{
  // integrals need to be promoted to double
  return _CUDA_VSTD::arg(static_cast<double>(__re));
}

// norm

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr _Tp norm(const complex<_Tp>& __c)
{
  if (_CUDA_VSTD::isinf(__c.real()))
  {
    return _CUDA_VSTD::abs(__c.real());
  }
  if (_CUDA_VSTD::isinf(__c.imag()))
  {
    return _CUDA_VSTD::abs(__c.imag());
  }
  return __c.real() * __c.real() + __c.imag() * __c.imag();
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr __cccl_complex_value_type<_Tp> norm(_Tp __re)
{
  return static_cast<__cccl_complex_value_type<_Tp>>(__re) * __re;
}

// conj

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr complex<_Tp> conj(const complex<_Tp>& __c)
{
  return complex<_Tp>(__c.real(), -__c.imag());
}

template <class _Tp>
[[nodiscard]] _CCCL_API constexpr __cccl_complex_complex_type<_Tp> conj(_Tp __re)
{
  return __cccl_complex_complex_type<_Tp>(__re);
}

// proj

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> proj(const complex<_Tp>& __c)
{
  complex<_Tp> __r = __c;
  if (_CUDA_VSTD::isinf(__c.real()) || _CUDA_VSTD::isinf(__c.imag()))
  {
    __r = complex<_Tp>(numeric_limits<_Tp>::infinity(), _CUDA_VSTD::__constexpr_copysign(_Tp(0), __c.imag()));
  }
  return __r;
}

_CCCL_TEMPLATE(class _Tp)
_CCCL_REQUIRES((_CCCL_TRAIT(is_floating_point, _Tp) || _CCCL_TRAIT(__is_extended_floating_point, _Tp)))
[[nodiscard]] _CCCL_API inline __cccl_complex_complex_type<_Tp> proj(_Tp __re)
{
  if (_CUDA_VSTD::isinf(__re))
  {
    __re = _CUDA_VSTD::abs(__re);
  }
  return complex<_Tp>(__re);
}

_CCCL_TEMPLATE(class _Tp)
_CCCL_REQUIRES(_CCCL_TRAIT(is_integral, _Tp))
[[nodiscard]] _CCCL_API inline __cccl_complex_complex_type<_Tp> proj(_Tp __re)
{
  return __cccl_complex_complex_type<_Tp>(__re);
}

// polar

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> polar(const _Tp& __rho, const _Tp& __theta = _Tp())
{
  if (_CUDA_VSTD::isnan(__rho) || _CUDA_VSTD::signbit(__rho))
  {
    return complex<_Tp>(numeric_limits<_Tp>::quiet_NaN(), numeric_limits<_Tp>::quiet_NaN());
  }
  if (_CUDA_VSTD::isnan(__theta))
  {
    if (_CUDA_VSTD::isinf(__rho))
    {
      return complex<_Tp>(__rho, __theta);
    }
    return complex<_Tp>(__theta, __theta);
  }
  if (_CUDA_VSTD::isinf(__theta))
  {
    if (_CUDA_VSTD::isinf(__rho))
    {
      return complex<_Tp>(__rho, numeric_limits<_Tp>::quiet_NaN());
    }
    return complex<_Tp>(numeric_limits<_Tp>::quiet_NaN(), numeric_limits<_Tp>::quiet_NaN());
  }
  _Tp __x = __rho * _CUDA_VSTD::cos(__theta);
  if (_CUDA_VSTD::isnan(__x))
  {
    __x = 0;
  }
  _Tp __y = __rho * _CUDA_VSTD::sin(__theta);
  if (_CUDA_VSTD::isnan(__y))
  {
    __y = 0;
  }
  return complex<_Tp>(__x, __y);
}

// log

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> log(const complex<_Tp>& __x)
{
  return complex<_Tp>(_CUDA_VSTD::log(_CUDA_VSTD::abs(__x)), _CUDA_VSTD::arg(__x));
}

// log10

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> log10(const complex<_Tp>& __x)
{
  return _CUDA_VSTD::log(__x) / _CUDA_VSTD::log(_Tp(10));
}

// sqrt

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> sqrt(const complex<_Tp>& __x)
{
  if (_CUDA_VSTD::isinf(__x.imag()))
  {
    return complex<_Tp>(numeric_limits<_Tp>::infinity(), __x.imag());
  }
  if (_CUDA_VSTD::isinf(__x.real()))
  {
    if (__x.real() > _Tp(0))
    {
      return complex<_Tp>(
        __x.real(), _CUDA_VSTD::isnan(__x.imag()) ? __x.imag() : _CUDA_VSTD::__constexpr_copysign(_Tp(0), __x.imag()));
    }
    return complex<_Tp>(_CUDA_VSTD::isnan(__x.imag()) ? __x.imag() : _Tp(0),
                        _CUDA_VSTD::__constexpr_copysign(__x.real(), __x.imag()));
  }
  return _CUDA_VSTD::polar(_CUDA_VSTD::sqrt(_CUDA_VSTD::abs(__x)), _CUDA_VSTD::arg(__x) / _Tp(2));
}

// exp

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> exp(const complex<_Tp>& __x)
{
  _Tp __i = __x.imag();
  if (__i == _Tp(0))
  {
    return complex<_Tp>(_CUDA_VSTD::exp(__x.real()), _CUDA_VSTD::__constexpr_copysign(_Tp(0), __x.imag()));
  }
  if (_CUDA_VSTD::isinf(__x.real()))
  {
    if (__x.real() < _Tp(0))
    {
      if (!_CUDA_VSTD::isfinite(__i))
      {
        __i = _Tp(1);
      }
    }
    else if (__i == _Tp(0) || !_CUDA_VSTD::isfinite(__i))
    {
      if (_CUDA_VSTD::isinf(__i))
      {
        __i = numeric_limits<_Tp>::quiet_NaN();
      }
      return complex<_Tp>(__x.real(), __i);
    }
  }
  _Tp __e = _CUDA_VSTD::exp(__x.real());
  return complex<_Tp>(__e * _CUDA_VSTD::cos(__i), __e * _CUDA_VSTD::sin(__i));
}

// pow

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> pow(const complex<_Tp>& __x, const complex<_Tp>& __y)
{
  return _CUDA_VSTD::exp(__y * _CUDA_VSTD::log(__x));
}

_CCCL_DIAG_PUSH
_CCCL_DIAG_SUPPRESS_MSVC(4244)

template <class _Tp, class _Up>
[[nodiscard]] _CCCL_API inline complex<common_type_t<_Tp, _Up>> pow(const complex<_Tp>& __x, const complex<_Up>& __y)
{
  using __result_type = complex<common_type_t<_Tp, _Up>>;
  return _CUDA_VSTD::pow(__result_type(__x), __result_type(__y));
}

_CCCL_TEMPLATE(class _Tp, class _Up)
_CCCL_REQUIRES((!__is_complex_v<_Up>) )
[[nodiscard]] _CCCL_API inline complex<common_type_t<_Tp, _Up>> pow(const complex<_Tp>& __x, const _Up& __y)
{
  using __result_type = complex<common_type_t<_Tp, _Up>>;
  return _CUDA_VSTD::pow(__result_type(__x), __result_type(__y));
}

_CCCL_TEMPLATE(class _Tp, class _Up)
_CCCL_REQUIRES((!__is_complex_v<_Tp>) )
[[nodiscard]] _CCCL_API inline complex<common_type_t<_Tp, _Up>> pow(const _Tp& __x, const complex<_Up>& __y)
{
  using __result_type = complex<common_type_t<_Tp, _Up>>;
  return _CUDA_VSTD::pow(__result_type(__x, 0), __result_type(__y));
}

_CCCL_DIAG_POP

// __sqr, computes pow(x, 2)

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> __sqr(const complex<_Tp>& __x)
{
  return complex<_Tp>((__x.real() - __x.imag()) * (__x.real() + __x.imag()), _Tp(2) * __x.real() * __x.imag());
}

// asinh

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> asinh(const complex<_Tp>& __x)
{
  const _Tp __pi(static_cast<_Tp>(atan2(+0., -0.)));
  if (_CUDA_VSTD::isinf(__x.real()))
  {
    if (_CUDA_VSTD::isnan(__x.imag()))
    {
      return __x;
    }
    if (_CUDA_VSTD::isinf(__x.imag()))
    {
      return complex<_Tp>(__x.real(), _CUDA_VSTD::__constexpr_copysign(__pi * _Tp(0.25), __x.imag()));
    }
    return complex<_Tp>(__x.real(), _CUDA_VSTD::__constexpr_copysign(_Tp(0), __x.imag()));
  }
  if (_CUDA_VSTD::isnan(__x.real()))
  {
    if (_CUDA_VSTD::isinf(__x.imag()))
    {
      return complex<_Tp>(__x.imag(), __x.real());
    }
    if (__x.imag() == _Tp(0))
    {
      return __x;
    }
    return complex<_Tp>(__x.real(), __x.real());
  }
  if (_CUDA_VSTD::isinf(__x.imag()))
  {
    return complex<_Tp>(_CUDA_VSTD::__constexpr_copysign(__x.imag(), __x.real()),
                        _CUDA_VSTD::__constexpr_copysign(__pi / _Tp(2), __x.imag()));
  }
  complex<_Tp> __z = _CUDA_VSTD::log(__x + _CUDA_VSTD::sqrt(_CUDA_VSTD::__sqr(__x) + _Tp(1)));
  return complex<_Tp>(_CUDA_VSTD::__constexpr_copysign(__z.real(), __x.real()),
                      _CUDA_VSTD::__constexpr_copysign(__z.imag(), __x.imag()));
}

// acosh

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> acosh(const complex<_Tp>& __x)
{
  const _Tp __pi(static_cast<_Tp>(atan2(+0., -0.)));
  if (_CUDA_VSTD::isinf(__x.real()))
  {
    if (_CUDA_VSTD::isnan(__x.imag()))
    {
      return complex<_Tp>(_CUDA_VSTD::abs(__x.real()), __x.imag());
    }
    if (_CUDA_VSTD::isinf(__x.imag()))
    {
      if (__x.real() > _Tp(0))
      {
        return complex<_Tp>(__x.real(), _CUDA_VSTD::__constexpr_copysign(__pi * _Tp(0.25), __x.imag()));
      }
      else
      {
        return complex<_Tp>(-__x.real(), _CUDA_VSTD::__constexpr_copysign(__pi * _Tp(0.75), __x.imag()));
      }
    }
    if (__x.real() < _Tp(0))
    {
      return complex<_Tp>(-__x.real(), _CUDA_VSTD::__constexpr_copysign(__pi, __x.imag()));
    }
    return complex<_Tp>(__x.real(), _CUDA_VSTD::__constexpr_copysign(_Tp(0), __x.imag()));
  }
  if (_CUDA_VSTD::isnan(__x.real()))
  {
    if (_CUDA_VSTD::isinf(__x.imag()))
    {
      return complex<_Tp>(_CUDA_VSTD::abs(__x.imag()), __x.real());
    }
    return complex<_Tp>(__x.real(), __x.real());
  }
  if (_CUDA_VSTD::isinf(__x.imag()))
  {
    return complex<_Tp>(_CUDA_VSTD::abs(__x.imag()), _CUDA_VSTD::__constexpr_copysign(__pi / _Tp(2), __x.imag()));
  }
  complex<_Tp> __z = _CUDA_VSTD::log(__x + _CUDA_VSTD::sqrt(_CUDA_VSTD::__sqr(__x) - _Tp(1)));
  return complex<_Tp>(_CUDA_VSTD::__constexpr_copysign(__z.real(), _Tp(0)),
                      _CUDA_VSTD::__constexpr_copysign(__z.imag(), __x.imag()));
}

// atanh

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> atanh(const complex<_Tp>& __x)
{
  const _Tp __pi(static_cast<_Tp>(atan2(+0., -0.)));
  if (_CUDA_VSTD::isinf(__x.imag()))
  {
    return complex<_Tp>(_CUDA_VSTD::__constexpr_copysign(_Tp(0), __x.real()),
                        _CUDA_VSTD::__constexpr_copysign(__pi / _Tp(2), __x.imag()));
  }
  if (_CUDA_VSTD::isnan(__x.imag()))
  {
    if (_CUDA_VSTD::isinf(__x.real()) || __x.real() == _Tp(0))
    {
      return complex<_Tp>(_CUDA_VSTD::__constexpr_copysign(_Tp(0), __x.real()), __x.imag());
    }
    return complex<_Tp>(__x.imag(), __x.imag());
  }
  if (_CUDA_VSTD::isnan(__x.real()))
  {
    return complex<_Tp>(__x.real(), __x.real());
  }
  if (_CUDA_VSTD::isinf(__x.real()))
  {
    return complex<_Tp>(_CUDA_VSTD::__constexpr_copysign(_Tp(0), __x.real()),
                        _CUDA_VSTD::__constexpr_copysign(__pi / _Tp(2), __x.imag()));
  }
  if (_CUDA_VSTD::abs(__x.real()) == _Tp(1) && __x.imag() == _Tp(0))
  {
    return complex<_Tp>(_CUDA_VSTD::__constexpr_copysign(numeric_limits<_Tp>::infinity(), __x.real()),
                        _CUDA_VSTD::__constexpr_copysign(_Tp(0), __x.imag()));
  }
  complex<_Tp> __z = _CUDA_VSTD::log((_Tp(1) + __x) / (_Tp(1) - __x)) / _Tp(2);
  return complex<_Tp>(_CUDA_VSTD::__constexpr_copysign(__z.real(), __x.real()),
                      _CUDA_VSTD::__constexpr_copysign(__z.imag(), __x.imag()));
}

// sinh

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> sinh(const complex<_Tp>& __x)
{
  if (_CUDA_VSTD::isinf(__x.real()) && !_CUDA_VSTD::isfinite(__x.imag()))
  {
    return complex<_Tp>(__x.real(), numeric_limits<_Tp>::quiet_NaN());
  }
  if (__x.real() == _Tp(0) && !_CUDA_VSTD::isfinite(__x.imag()))
  {
    return complex<_Tp>(__x.real(), numeric_limits<_Tp>::quiet_NaN());
  }
  if (__x.imag() == _Tp(0) && !_CUDA_VSTD::isfinite(__x.real()))
  {
    return __x;
  }
  return complex<_Tp>(_CUDA_VSTD::sinh(__x.real()) * _CUDA_VSTD::cos(__x.imag()),
                      _CUDA_VSTD::cosh(__x.real()) * _CUDA_VSTD::sin(__x.imag()));
}

// cosh

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> cosh(const complex<_Tp>& __x)
{
  if (_CUDA_VSTD::isinf(__x.real()) && !_CUDA_VSTD::isfinite(__x.imag()))
  {
    return complex<_Tp>(_CUDA_VSTD::abs(__x.real()), numeric_limits<_Tp>::quiet_NaN());
  }
  if (__x.real() == _Tp(0) && !_CUDA_VSTD::isfinite(__x.imag()))
  {
    return complex<_Tp>(numeric_limits<_Tp>::quiet_NaN(), __x.real());
  }
  if (__x.real() == _Tp(0) && __x.imag() == _Tp(0))
  {
    return complex<_Tp>(_Tp(1), __x.imag());
  }
  if (__x.imag() == _Tp(0) && !_CUDA_VSTD::isfinite(__x.real()))
  {
    return complex<_Tp>(_CUDA_VSTD::abs(__x.real()), __x.imag());
  }
  return complex<_Tp>(_CUDA_VSTD::cosh(__x.real()) * _CUDA_VSTD::cos(__x.imag()),
                      _CUDA_VSTD::sinh(__x.real()) * _CUDA_VSTD::sin(__x.imag()));
}

// tanh

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> tanh(const complex<_Tp>& __x)
{
  if (_CUDA_VSTD::isinf(__x.real()))
  {
    if (!_CUDA_VSTD::isfinite(__x.imag()))
    {
      return complex<_Tp>(_CUDA_VSTD::__constexpr_copysign(_Tp(1), __x.real()), _Tp(0));
    }
    return complex<_Tp>(_CUDA_VSTD::__constexpr_copysign(_Tp(1), __x.real()),
                        _CUDA_VSTD::__constexpr_copysign(_Tp(0), _CUDA_VSTD::sin(_Tp(2) * __x.imag())));
  }
  if (_CUDA_VSTD::isnan(__x.real()) && __x.imag() == _Tp(0))
  {
    return __x;
  }
  _Tp __2r(_Tp(2) * __x.real());
  _Tp __2i(_Tp(2) * __x.imag());
  _Tp __d(_CUDA_VSTD::cosh(__2r) + _CUDA_VSTD::cos(__2i));
  _Tp __2rsh(_CUDA_VSTD::sinh(__2r));
  if (_CUDA_VSTD::isinf(__2rsh) && _CUDA_VSTD::isinf(__d))
  {
    return complex<_Tp>(__2rsh > _Tp(0) ? _Tp(1) : _Tp(-1), __2i > _Tp(0) ? _Tp(0) : _Tp(-0.));
  }
  return complex<_Tp>(__2rsh / __d, _CUDA_VSTD::sin(__2i) / __d);
}

// asin

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> asin(const complex<_Tp>& __x)
{
  complex<_Tp> __z = _CUDA_VSTD::asinh(complex<_Tp>(-__x.imag(), __x.real()));
  return complex<_Tp>(__z.imag(), -__z.real());
}

// acos

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> acos(const complex<_Tp>& __x)
{
  const _Tp __pi(static_cast<_Tp>(atan2(+0., -0.)));
  if (_CUDA_VSTD::isinf(__x.real()))
  {
    if (_CUDA_VSTD::isnan(__x.imag()))
    {
      return complex<_Tp>(__x.imag(), __x.real());
    }
    if (_CUDA_VSTD::isinf(__x.imag()))
    {
      if (__x.real() < _Tp(0))
      {
        return complex<_Tp>(_Tp(0.75) * __pi, -__x.imag());
      }
      return complex<_Tp>(_Tp(0.25) * __pi, -__x.imag());
    }
    if (__x.real() < _Tp(0))
    {
      return complex<_Tp>(__pi, _CUDA_VSTD::signbit(__x.imag()) ? -__x.real() : __x.real());
    }
    return complex<_Tp>(_Tp(0), _CUDA_VSTD::signbit(__x.imag()) ? __x.real() : -__x.real());
  }
  if (_CUDA_VSTD::isnan(__x.real()))
  {
    if (_CUDA_VSTD::isinf(__x.imag()))
    {
      return complex<_Tp>(__x.real(), -__x.imag());
    }
    return complex<_Tp>(__x.real(), __x.real());
  }
  if (_CUDA_VSTD::isinf(__x.imag()))
  {
    return complex<_Tp>(__pi / _Tp(2), -__x.imag());
  }
  if (__x.real() == _Tp(0) && (__x.imag() == _Tp(0) || _CUDA_VSTD::isnan(__x.imag())))
  {
    return complex<_Tp>(__pi / _Tp(2), -__x.imag());
  }
  complex<_Tp> __z = _CUDA_VSTD::log(__x + _CUDA_VSTD::sqrt(_CUDA_VSTD::__sqr(__x) - _Tp(1)));
  if (_CUDA_VSTD::signbit(__x.imag()))
  {
    return complex<_Tp>(_CUDA_VSTD::abs(__z.imag()), _CUDA_VSTD::abs(__z.real()));
  }
  return complex<_Tp>(_CUDA_VSTD::abs(__z.imag()), -_CUDA_VSTD::abs(__z.real()));
}

// atan

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> atan(const complex<_Tp>& __x)
{
  complex<_Tp> __z = _CUDA_VSTD::atanh(complex<_Tp>(-__x.imag(), __x.real()));
  return complex<_Tp>(__z.imag(), -__z.real());
}

// sin

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> sin(const complex<_Tp>& __x)
{
  complex<_Tp> __z = _CUDA_VSTD::sinh(complex<_Tp>(-__x.imag(), __x.real()));
  return complex<_Tp>(__z.imag(), -__z.real());
}

// cos

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> cos(const complex<_Tp>& __x)
{
  return _CUDA_VSTD::cosh(complex<_Tp>(-__x.imag(), __x.real()));
}

// tan

template <class _Tp>
[[nodiscard]] _CCCL_API inline complex<_Tp> tan(const complex<_Tp>& __x)
{
  complex<_Tp> __z = _CUDA_VSTD::tanh(complex<_Tp>(-__x.imag(), __x.real()));
  return complex<_Tp>(__z.imag(), -__z.real());
}

template <class _Tp>
struct tuple_size<complex<_Tp>> : _CUDA_VSTD::integral_constant<size_t, 2>
{};

template <size_t _Index, class _Tp>
  struct tuple_element<_Index, complex<_Tp>> : _CUDA_VSTD::enable_if < _Index<2, _Tp>
{};

template <class _Tp>
struct __get_complex_impl
{
  template <size_t _Index>
  static _CCCL_API constexpr _Tp& get(complex<_Tp>& __z) noexcept
  {
    return (_Index == 0) ? __z.__re_ : __z.__im_;
  }

  template <size_t _Index>
  static _CCCL_API constexpr _Tp&& get(complex<_Tp>&& __z) noexcept
  {
    return _CUDA_VSTD::move((_Index == 0) ? __z.__re_ : __z.__im_);
  }

  template <size_t _Index>
  static _CCCL_API constexpr const _Tp& get(const complex<_Tp>& __z) noexcept
  {
    return (_Index == 0) ? __z.__re_ : __z.__im_;
  }

  template <size_t _Index>
  static _CCCL_API constexpr const _Tp&& get(const complex<_Tp>&& __z) noexcept
  {
    return _CUDA_VSTD::move((_Index == 0) ? __z.__re_ : __z.__im_);
  }
};

template <size_t _Index, class _Tp>
[[nodiscard]] _CCCL_API constexpr _Tp& get(complex<_Tp>& __z) noexcept
{
  static_assert(_Index < 2, "Index value is out of range");

  return __get_complex_impl<_Tp>::template get<_Index>(__z);
}

template <size_t _Index, class _Tp>
[[nodiscard]] _CCCL_API constexpr _Tp&& get(complex<_Tp>&& __z) noexcept
{
  static_assert(_Index < 2, "Index value is out of range");

  return __get_complex_impl<_Tp>::template get<_Index>(_CUDA_VSTD::move(__z));
}

template <size_t _Index, class _Tp>
[[nodiscard]] _CCCL_API constexpr const _Tp& get(const complex<_Tp>& __z) noexcept
{
  static_assert(_Index < 2, "Index value is out of range");

  return __get_complex_impl<_Tp>::template get<_Index>(__z);
}

template <size_t _Index, class _Tp>
[[nodiscard]] _CCCL_API constexpr const _Tp&& get(const complex<_Tp>&& __z) noexcept
{
  static_assert(_Index < 2, "Index value is out of range");

  return __get_complex_impl<_Tp>::template get<_Index>(_CUDA_VSTD::move(__z));
}

#if !_CCCL_COMPILER(NVRTC)
template <class _Tp, class _CharT, class _Traits>
::std::basic_istream<_CharT, _Traits>& operator>>(::std::basic_istream<_CharT, _Traits>& __is, complex<_Tp>& __x)
{
  ::std::complex<_Tp> __temp;
  __is >> __temp;
  __x = __temp;
  return __is;
}

template <class _Tp, class _CharT, class _Traits>
::std::basic_ostream<_CharT, _Traits>& operator<<(::std::basic_ostream<_CharT, _Traits>& __os, const complex<_Tp>& __x)
{
  return __os << static_cast<::std::complex<_Tp>>(__x);
}
#endif // !_CCCL_COMPILER(NVRTC)

#ifdef _LIBCUDACXX_HAS_STL_LITERALS
// Literal suffix for complex number literals [complex.literals]

_CCCL_DIAG_PUSH
_CCCL_DIAG_SUPPRESS_GCC("-Wliteral-suffix")
_CCCL_DIAG_SUPPRESS_CLANG("-Wuser-defined-literals")
_CCCL_DIAG_SUPPRESS_MSVC(4455)

inline namespace literals
{
inline namespace complex_literals
{
#  if !_CCCL_CUDA_COMPILER(NVCC) && !_CCCL_COMPILER(NVRTC)
// NOTE: if you get a warning from GCC <7 here that "literal operator suffixes not preceded by ‘_’ are reserved for
// future standardization" then we are sorry. The warning was implemented before GCC 7, but can only be disabled since
// GCC 7. See also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69523
_CCCL_API constexpr complex<long double> operator""il(long double __im)
{
  return {0.0l, __im};
}
_CCCL_API constexpr complex<long double> operator""il(unsigned long long __im)
{
  return {0.0l, static_cast<long double>(__im)};
}

_CCCL_API constexpr complex<double> operator""i(long double __im)
{
  return {0.0, static_cast<double>(__im)};
}

_CCCL_API constexpr complex<double> operator""i(unsigned long long __im)
{
  return {0.0, static_cast<double>(__im)};
}

_CCCL_API constexpr complex<float> operator""if(long double __im)
{
  return {0.0f, static_cast<float>(__im)};
}

_CCCL_API constexpr complex<float> operator""if(unsigned long long __im)
{
  return {0.0f, static_cast<float>(__im)};
}
#  else // ^^^ !_CCCL_CUDA_COMPILER(NVCC) && !_CCCL_COMPILER(NVRTC) ^^^ / vvv other compilers vvv
_CCCL_API constexpr complex<double> operator""i(double __im)
{
  return {0.0, static_cast<double>(__im)};
}

_CCCL_API constexpr complex<double> operator""i(unsigned long long __im)
{
  return {0.0, static_cast<double>(__im)};
}

_CCCL_API constexpr complex<float> operator""if(double __im)
{
  return {0.0f, static_cast<float>(__im)};
}

_CCCL_API constexpr complex<float> operator""if(unsigned long long __im)
{
  return {0.0f, static_cast<float>(__im)};
}
#  endif // other compilers
} // namespace complex_literals
} // namespace literals

_CCCL_DIAG_POP

#endif // _LIBCUDACXX_HAS_STL_LITERALS

_LIBCUDACXX_END_NAMESPACE_STD

#include <cuda/std/__cccl/epilogue.h>

#if _LIBCUDACXX_HAS_NVFP16()
#  include <cuda/std/__complex/nvfp16.h>
#endif // _LIBCUDACXX_HAS_NVFP16()

#if _LIBCUDACXX_HAS_NVBF16()
#  include <cuda/std/__complex/nvbf16.h>
#endif // _LIBCUDACXX_HAS_NVBF16()

#undef _LIBCUDACXX_ACCESS_STD_COMPLEX_REAL
#undef _LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG

#endif // _LIBCUDACXX_COMPLEX
